﻿using Devil;
using GameToolkit.UI;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace GameToolkit
{
    [RequireComponent(typeof(ScrollRect)), DisallowMultipleComponent]
    public class ScrollViewFocus : MonoBehaviour
    {
        public enum EFocusMode
        {
            Page,
            Element,
        }

        public EFocusMode m_FocusMode;
        public float m_BoarderSize = 5f;
        public Vector2 m_BoarderOffset;

        public float m_MoveStrength = 1f;
        ScrollRect mScroll;
        bool mFocused;

        Transform mFocusTarget;
        Vector2 mFinalContentPos;
        Vector2 mContentSize;
        float mStrengthMul;
        //Vector2 mStartPos;
        [SerializeField, EnableEdit(EEnableEditMode.EditorOnly)]
        SelectableNavigation m_SelGroup;
        public bool IsMoving { get { return !mFocused; } }

        private void OnEnable()
        {
            mScroll = GetComponent<ScrollRect>();
            mFocused = !(GameInput.ActiveControllerType == EControllerType.Mouse && GameInput.IsCurrentAxisAction);
            if (m_SelGroup == null)
                m_SelGroup = GetComponentInChildren<SelectableNavigation>();
            if (m_SelGroup != null)
            {
                m_SelGroup.OnSelectableChagned += SelectedChanged;
                //mFocused = true;
            }
        }

        private void OnDisable()
        {
            mFocusTarget = null;
            if (m_SelGroup != null)
                m_SelGroup.OnSelectableChagned -= SelectedChanged;
        }

        public void SelectedChanged(Selectable obj)
        {
            mFocused = !(GameInput.ActiveControllerType == EControllerType.Mouse && GameInput.IsCurrentAxisAction);
        }

        private void LateUpdate()
        {
            if (GameInput.ActiveControllerType == EControllerType.Mouse && GameInput.IsCurrentAxisAction /*&& Input.mouseScrollDelta.sqrMagnitude > 0.001f*/)
            {
                mFocusTarget = null;
                mFocused = false;
            }
            else if (GameInput.IsAnyButtonUp && GameInput.ActiveControllerType != EControllerType.Mouse)
            {
                mFocused = true;
            }
            if (mFocused)
            {
                FocusTarget(m_MoveStrength * mStrengthMul * Time.unscaledDeltaTime);
            }
        }

        Rect GetRect(Transform trans)
        {
            Rect rect = trans is RectTransform ? ((RectTransform)trans).rect : new Rect(Vector2.zero, Vector2.zero);
            return rect;
        }

        void SetFocusTarget(Transform target)
        {
            if (target != null)
            {
                mContentSize = mScroll.content.rect.size;
                var rect = mScroll.viewport.rect;
                var content = SelectableNavigation.GetLocalRect(mScroll.viewport, mScroll.content, mScroll.content.rect);
                var selHandler = target.GetComponentInParent<ISelectableRectModifier>();
                SelectableRect sr = default;
                if (selHandler != null)
                {
                    sr = selHandler.GetSelectableRect(target.gameObject, mScroll.viewport);
                }
                var itemRect = sr.isVaild ? sr.rect : SelectableNavigation.GetLocalRect(mScroll.viewport, target, GetRect(target));
                //GizmosUtil.DebugCube(mScroll.viewport, new Bounds(itemRect.center, itemRect.size), Color.red, 3f);
                //   GetViewportRect(target);
                //var bd = RectTransformUtility.CalculateRelativeRectTransformBounds(mScroll.viewport, target);
                Vector2 delta = Vector2.zero;
                if (m_FocusMode == EFocusMode.Page)
                {
                    var pagex = (itemRect.xMin - content.xMin) / rect.width;
                    var pagey = (content.yMax - itemRect.yMax) / rect.height;
                    itemRect.xMin = content.xMin + Mathf.Floor(pagex) * rect.width;
                    itemRect.yMax = content.yMax - Mathf.Floor(pagey) * rect.height;
                    itemRect.yMin = itemRect.yMax - rect.height;
                    itemRect.xMax = itemRect.xMin + rect.width;
                }
                else
                {
                    var boarder = new Vector2(m_BoarderSize, m_BoarderSize);
                    itemRect.position += -boarder + new Vector2(m_BoarderOffset.x * boarder.x, m_BoarderOffset.y * boarder.y);
                    itemRect.size += boarder * 2f;
                    if (itemRect.xMin < content.xMin)
                        itemRect.xMin = content.xMin;
                    if (itemRect.xMax > content.xMax)
                        itemRect.xMax = content.xMax;
                    if (itemRect.yMin < content.yMin)
                        itemRect.yMin = content.yMin;
                    if (itemRect.yMax > content.yMax)
                        itemRect.yMax = content.yMax;
                }
                var dsize = (itemRect.size - rect.size) * 0.5f;
                if (dsize.x > 0)
                {
                    itemRect.xMin += dsize.x;
                    itemRect.xMax -= dsize.x;
                }
                if (dsize.y > 0)
                {
                    itemRect.yMin += dsize.y;
                    itemRect.yMax -= dsize.y;
                }
                if (content.width > rect.width && mScroll.horizontal)
                {
                    if (itemRect.xMin < rect.xMin)
                    {
                        delta.x += rect.xMin - itemRect.xMin;
                    }
                    else if (itemRect.xMax > rect.xMax)
                    {
                        delta.x += rect.xMax - itemRect.xMax;
                    }
                }

                if (content.size.y > rect.height && mScroll.vertical)
                {
                    if (itemRect.yMax > rect.yMax)
                    {
                        delta.y += rect.yMax - itemRect.yMax;
                    }
                    else if (itemRect.yMin < rect.yMin)
                    {
                        delta.y += rect.yMin - itemRect.yMin;
                    }
                }
                mFinalContentPos = content.min + delta;
                float f;
                float fund;
                if (mScroll.vertical && mScroll.horizontal)
                {
                    f = Mathf.Max(content.size.y - rect.height, content.size.x - rect.width);
                    fund = Mathf.Max(Mathf.Abs(delta.x), Mathf.Abs(delta.y));
                }
                else if (mScroll.horizontal)
                {
                    f = content.size.x - rect.width;
                    fund = Mathf.Abs(delta.x);
                }
                else
                {
                    f = content.size.y - rect.height;
                    fund = Mathf.Abs(delta.y);
                }
                mStrengthMul = f > 0 ? (4f * fund / f) : 1;
            }
            mFocusTarget = target;
        }

        void FocusTarget(float strength)
        {
            if (mScroll == null || mScroll.viewport == null)
                return;
            var sel = EventSystem.current == null ? null : EventSystem.current.currentSelectedGameObject;
            var view = sel == null ? null : sel.transform as RectTransform;
            if (view != null && view.IsChildOf(mScroll.content))
            {
                var size = mScroll.content.rect.size;
                if (mFocusTarget != view || MathExt.IsDifferent(size, mContentSize, 1))
                    SetFocusTarget(view);
                //if (view != null)
                //{
                var rect = mScroll.viewport.rect;
                var content = SelectableNavigation.GetLocalRect(mScroll.viewport, mScroll.content, mScroll.content.rect);
                //RectTransformUtility.CalculateRelativeRectTransformBounds(mScroll.viewport, mScroll.content);
                var np = mScroll.normalizedPosition;
                Vector2 pos;
                if (content.height > rect.height && mScroll.vertical)
                    pos.y = (rect.yMin - mFinalContentPos.y) / (content.height - rect.height);
                else
                    pos.y = np.y;
                if (content.width > rect.width && mScroll.horizontal)
                    pos.x = (rect.xMin - mFinalContentPos.x) / (content.width - rect.width);
                else
                    pos.x = np.x;
                mScroll.normalizedPosition = Vector2.MoveTowards(np, pos, strength);
                if (Vector2.SqrMagnitude(mScroll.normalizedPosition - pos) < 0.00001f)
                    mFocused = false;
                //}
            }
        }

        [ContextMenu("Focus Target")]
        void FocusTarget()
        {
            FocusTarget(1f);
        }

#if UNITY_EDITOR
        private void OnDrawGizmosSelected()
        {
            if (mScroll == null)
                mScroll = GetComponent<ScrollRect>();
            if (mScroll == null || mScroll.content == null)
                return;
            var viewport = mScroll.viewport;
            var rect = viewport.rect;
            Gizmos.matrix = viewport.transform.localToWorldMatrix;
            Gizmos.color = Color.green;
            GizmosUtil.DrawWiredCube(rect.center, rect.size);

            var sel = EventSystem.current == null ? null : EventSystem.current.currentSelectedGameObject;
            var view = sel == null ? null : sel.transform;
            if (view != null && view.IsChildOf(viewport))
            {
                var bd = RectTransformUtility.CalculateRelativeRectTransformBounds(mScroll.viewport, view);
                Gizmos.color = Color.red;
                GizmosUtil.DrawWiredCube(bd.center, bd.size);
            }
        }
#endif
    }
}